package org.modelcc.io.java.visitor;

import java.lang.reflect.Field;
import java.util.logging.Level;

import org.modelcc.Multiplicity;
import org.modelcc.io.java.JavaModelReader;
import org.modelcc.io.java.JavaLanguageVisitor;
import org.modelcc.language.metamodel.MemberCollection;
import org.modelcc.language.metamodel.LanguageMember;
import org.modelcc.lexer.recognizer.PatternRecognizer;

/**
 * Java model reader: Class visitor for @Multiplicity
 * 
 * @author Fernando Berzal (berzal@acm.org)
 */
public class MultiplicityVisitor extends JavaLanguageVisitor 
{	
	public MultiplicityVisitor(JavaModelReader reader)
	{
		super(reader);
	}
	
	@Override
	public void visit (Field field, LanguageMember member) 
	{
    	int minimumMultiplicity = Multiplicity.UNDEFINED;
    	int maximumMultiplicity = Multiplicity.UNDEFINED;

    	if (field.isAnnotationPresent(Multiplicity.class)) {
    		minimumMultiplicity = field.getAnnotation(Multiplicity.class).minimum();
    		if (minimumMultiplicity!=Multiplicity.UNDEFINED) {
    			if (minimumMultiplicity < 0) {
    				log(Level.SEVERE, "In field \"{0}\" of class \"{1}\": The @Multiplicity minimum value has to be 0 or higher.", 
    						new Object[]{field.getName(), field.getDeclaringClass().getCanonicalName()});
    				minimumMultiplicity = Multiplicity.UNDEFINED;
    			}
    		}
    		maximumMultiplicity = field.getAnnotation(Multiplicity.class).maximum();
    		if (maximumMultiplicity!=Multiplicity.UNDEFINED) {
    			if (maximumMultiplicity < 0) {
    				log(Level.SEVERE, "In field \"{0}\" of class \"{1}\": The @Multiplicity maximum value has to be 0 or higher.", 
    						new Object[]{field.getName(), field.getDeclaringClass().getCanonicalName()});
    				maximumMultiplicity = Multiplicity.UNDEFINED;
    			}
    		}
    		if (minimumMultiplicity!=Multiplicity.UNDEFINED && maximumMultiplicity!=Multiplicity.UNDEFINED) {
    			if (minimumMultiplicity>maximumMultiplicity) {
    				log(Level.SEVERE, "In field \"{0}\" of class \"{1}\": The @Multiplicity maximum value has to be the same or higher than the minimum value.", 
    						new Object[]{field.getName(), field.getDeclaringClass().getCanonicalName()});
    				maximumMultiplicity = Multiplicity.UNDEFINED;
    			}
    		}
    		if (!(member instanceof MemberCollection)) {
    			log(Level.SEVERE, "In field \"{0}\" of class \"{1}\": The @Multiplicity minimum or maximum values can only be set for container contents.", 
    					new Object[]{field.getName(), field.getDeclaringClass().getCanonicalName()});
    			maximumMultiplicity = Multiplicity.UNDEFINED;
    			minimumMultiplicity = Multiplicity.UNDEFINED;
    		}
    	}
    	
    	if (member instanceof MemberCollection) {
    		
    		if (minimumMultiplicity == Multiplicity.UNDEFINED || minimumMultiplicity == 0) {
    			minimumMultiplicity = 0;
    		}

    		if (member.isOptional() && canBeEmpty(member) && minimumMultiplicity==0)
    			minimumMultiplicity=1;

    		((MemberCollection)member).setMinimumMultiplicity(minimumMultiplicity);
    		((MemberCollection)member).setMaximumMultiplicity(maximumMultiplicity);
    	}		
	}

	private boolean canBeEmpty(LanguageMember member) 
	{
		boolean empty = true;
		if (member.getPrefix() != null) {
			for (PatternRecognizer prx : member.getPrefix()) {
				if (prx.read("",0)==null) {
					empty = false;
				}
			}
		}
		if (member.getSuffix() != null) {
			for (PatternRecognizer prx : member.getSuffix()) {
				if (prx.read("",0)==null) {
					empty = false;
				}
			}
		}
		return empty;
	}

}
